#------------------------------------------------------------------------------
#
# Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution.  The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php.
#
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
# Module Name:
#
#   SmiEntry.S
#
# Abstract:
#
#   Code template of the SMI handler for a particular processor
#
#------------------------------------------------------------------------------

ASM_GLOBAL  ASM_PFX(gcStmSmiHandlerTemplate)
ASM_GLOBAL  ASM_PFX(gcStmSmiHandlerSize)
ASM_GLOBAL  ASM_PFX(gcStmSmiHandlerOffset)
ASM_GLOBAL  ASM_PFX(gStmSmiCr3)
ASM_GLOBAL  ASM_PFX(gStmSmiStack)
ASM_GLOBAL  ASM_PFX(gStmSmbase)
ASM_GLOBAL  ASM_PFX(gStmXdSupported)
ASM_GLOBAL  ASM_PFX(FeaturePcdGet (PcdCpuSmmStackGuard))
ASM_GLOBAL  ASM_PFX(gStmSmiHandlerIdtr)

.equ            MSR_IA32_MISC_ENABLE, 0x1A0
.equ            MSR_EFER, 0xc0000080
.equ            MSR_EFER_XD, 0x800

#
# Constants relating to TXT_PROCESSOR_SMM_DESCRIPTOR
#
.equ            DSC_OFFSET, 0xfb00
.equ            DSC_GDTPTR, 0x48
.equ            DSC_GDTSIZ, 0x50
.equ            DSC_CS, 0x14
.equ            DSC_DS, 0x16
.equ            DSC_SS, 0x18
.equ            DSC_OTHERSEG, 0x1A

.equ            PROTECT_MODE_CS, 0x08
.equ            PROTECT_MODE_DS, 0x20
.equ            TSS_SEGMENT,     0x40

    .text
ASM_PFX(gcStmSmiHandlerTemplate):

_StmSmiEntryPoint:
    .byte 0xbb                          # mov bx, imm16
    .word _StmGdtDesc - _StmSmiEntryPoint + 0x8000
    .byte 0x2e,0xa1                     # mov ax, cs:[offset16]
    .word DSC_OFFSET + DSC_GDTSIZ
    decl    %eax
    movl    %eax, %cs:(%edi)            # mov cs:[bx], ax
    .byte 0x66,0x2e,0xa1                # mov eax, cs:[offset16]
    .word   DSC_OFFSET + DSC_GDTPTR
    movw    %ax, %cs:2(%edi)
    movw    %ax, %bp                    # ebp = GDT base
    .byte 0x66
    lgdt    %cs:(%edi)
# Patch ProtectedMode Segment
    .byte   0xb8                        # mov ax, imm16
    .word   PROTECT_MODE_CS             # set AX for segment directly
    movl    %eax, %cs:-2(%edi)          # mov cs:[bx - 2], ax
# Patch ProtectedMode entry
    .byte 0x66, 0xbf                    # mov edi, SMBASE
ASM_PFX(gStmSmbase): .space 4
    .byte 0x67
    lea     ((Start32bit - _StmSmiEntryPoint) + 0x8000)(%edi), %ax
    movw     %ax, %cs:-6(%edi)
    movl    %cr0, %ebx
    .byte 0x66
    andl    $0x9ffafff3, %ebx
    .byte 0x66
    orl     $0x23, %ebx
    movl    %ebx, %cr0
    .byte 0x66,0xea
    .space  4
    .space  2
_StmGdtDesc:   .space 4
            .space 2

Start32bit:
    movw    $PROTECT_MODE_DS, %ax
    movl    %eax,%ds
    movl    %eax,%es
    movl    %eax,%fs
    movl    %eax,%gs
    movl    %eax,%ss
    .byte   0xbc                          # mov esp, imm32
ASM_PFX(gStmSmiStack): .space 4
    movl    $ASM_PFX(gStmSmiHandlerIdtr), %eax
    lidt    (%eax)
    jmp     ProtFlatMode

ProtFlatMode:
    .byte   0xb8                           # mov eax, imm32
ASM_PFX(gStmSmiCr3): .space 4
    movl    %eax, %cr3
#
# Need to test for CR4 specific bit support
#
    movl    $1, %eax
    cpuid                                  # use CPUID to determine if specific CR4 bits are supported
    xorl    %eax, %eax                     # Clear EAX
    testl   $BIT2, %edx                    # Check for DE capabilities
    jz      L8
    orl     $BIT3, %eax
L8:
    testl   $BIT6, %edx                    # Check for PAE capabilities
    jz      L9
    orl     $BIT5, %eax
L9:
    testl   $BIT7, %edx                    # Check for MCE capabilities
    jz      L10
    orl     $BIT6, %eax
L10:
    testl   $BIT24, %edx                   # Check for FXSR capabilities
    jz      L11
    orl     $BIT9, %eax
L11:
    testl   $BIT25, %edx                   # Check for SSE capabilities
    jz      L12
    orl     $BIT10, %eax
L12:                                       # as cr4.PGE is not set here, refresh cr3
    movl    %eax, %cr4                     # in PreModifyMtrrs() to flush TLB.

    cmpb    $0, ASM_PFX(FeaturePcdGet (PcdCpuSmmStackGuard))
    jz      L5
# Load TSS
    movb    $0x89, (TSS_SEGMENT + 5)(%ebp) # clear busy flag
    movl    $TSS_SEGMENT, %eax
    ltrw    %ax
L5:

# enable NXE if supported
    .byte   0xb0                           # mov al, imm8
ASM_PFX(gStmXdSupported): .byte 1
    cmpb    $0, %al
    jz      SkipXd
#
# Check XD disable bit
#
    movl    $MSR_IA32_MISC_ENABLE, %ecx
    rdmsr
    pushl   %edx                           # save MSR_IA32_MISC_ENABLE[63-32]
    testl   $BIT2, %edx                    # MSR_IA32_MISC_ENABLE[34]
    jz      L13
    andw    $0x0FFFB, %dx                  # clear XD Disable bit if it is set
    wrmsr
L13:
    movl    $MSR_EFER, %ecx
    rdmsr
    orw     $MSR_EFER_XD,%ax               # enable NXE
    wrmsr
    jmp     XdDone
SkipXd:
    subl    $4, %esp
XdDone:

    movl    %cr0, %ebx
    orl     $0x080010023, %ebx             # enable paging + WP + NE + MP + PE
    movl    %ebx, %cr0
    leal    DSC_OFFSET(%edi),%ebx
    movw    DSC_DS(%ebx),%ax
    movl    %eax, %ds
    movw    DSC_OTHERSEG(%ebx),%ax
    movl    %eax, %es
    movl    %eax, %fs
    movl    %eax, %gs
    movw    DSC_SS(%ebx),%ax
    movl    %eax, %ss

CommonHandler:
    movl    4(%esp), %ebx

    pushl   %ebx
    movl    $ASM_PFX(CpuSmmDebugEntry), %eax
    call    *%eax
    addl    $4, %esp

    pushl   %ebx
    movl    $ASM_PFX(SmiRendezvous), %eax
    call    *%eax
    addl    $4, %esp

    pushl   %ebx
    movl    $ASM_PFX(CpuSmmDebugExit), %eax
    call    *%eax
    addl    $4, %esp

    movl    $ASM_PFX(gStmXdSupported), %eax
    movb    (%eax), %al
    cmpb    $0, %al
    jz      L16
    popl    %edx                        # get saved MSR_IA32_MISC_ENABLE[63-32]
    testl   $BIT2, %edx
    jz      L16
    movl    $MSR_IA32_MISC_ENABLE, %ecx
    rdmsr
    orw     $BIT2, %dx                  # set XD Disable bit if it was set before entering into SMM
    wrmsr

L16:
    rsm

_StmSmiHandler:
#
# Check XD disable bit
#
    xorl    %esi, %esi
    movl    $ASM_PFX(gStmXdSupported), %eax
    movb    (%eax), %al
    cmpb    $0, %al
    jz      StmXdDone
    movl    $MSR_IA32_MISC_ENABLE, %ecx
    rdmsr
    movl    %edx, %esi                     # save MSR_IA32_MISC_ENABLE[63-32]
    testl   $BIT2, %edx                    # MSR_IA32_MISC_ENABLE[34]
    jz      L14
    andw    $0x0FFFB, %dx                  # clear XD Disable bit if it is set
    wrmsr
L14:
    movl    $MSR_EFER, %ecx
    rdmsr
    orw     $MSR_EFER_XD,%ax               # enable NXE
    wrmsr
StmXdDone:
    push    %esi

    # below step is needed, because STM does not run above code.
    # we have to run below code to set IDT/CR0/CR4
    movl    $ASM_PFX(gStmSmiHandlerIdtr), %eax
    lidt    (%eax)

    movl    %cr0, %eax
    orl     $0x80010023, %eax           # enable paging + WP + NE + MP + PE
    movl    %eax, %cr0
#
# Need to test for CR4 specific bit support
#
    movl    $1, %eax
    cpuid                               # use CPUID to determine if specific CR4 bits are supported
    movl    %cr4, %eax                  # init EAX
    testl   $BIT2, %edx                 # Check for DE capabilities
    jz      L28
    orl     $BIT3, %eax
L28:
    testl   $BIT6, %edx                 # Check for PAE capabilities
    jz      L29
    orl     $BIT5, %eax
L29:
    testl   $BIT7, %edx                 # Check for MCE capabilities
    jz      L30
    orl     $BIT6, %eax
L30:
    testl   $BIT24, %edx                # Check for FXSR capabilities
    jz      L31
    orl     $BIT9, %eax
L31:
    testl   $BIT25, %edx                # Check for SSE capabilities
    jz      L32
    orl     $BIT10, %eax
L32:                                    # as cr4.PGE is not set here, refresh cr3
    movl    %eax, %cr4                  # in PreModifyMtrrs() to flush TLB.
    # STM init finish
    jmp     CommonHandler


ASM_PFX(gcStmSmiHandlerSize)  :  .word      . - _StmSmiEntryPoint
ASM_PFX(gcStmSmiHandlerOffset):  .word      _StmSmiHandler - _StmSmiEntryPoint

